package com.cenxi.utils.filter;

import com.cenxi.common.exceptions.ServiceException;
import com.cenxi.config.RedisService;
import com.cenxi.dao.entity.Account;
import com.cenxi.dao.service.AccountService;
import com.cenxi.utils.SpringUtils;
import com.cenxi.utils.constants.Constants;
import com.cenxi.utils.constants.TokenConstant;
import com.cenxi.utils.jwt.JwtUtils;
import com.cenxi.utils.subject.LoginSubject;
import com.cenxi.utils.subject.SecurityContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * @Author cenxi
 * @Date 2023/8/16 14:26
 * @Version 1.0
 */
@Slf4j
@Component
public class CustomerEnableIntercept implements HandlerInterceptor {

    private static final long MILLIS_MINUTE_TEN = 3000L;
    /**
     * 方法执行前拦截
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        log.info("开始鉴权拦截,url: {}", request.getRequestURI());
        String token = request.getHeader(TokenConstant.AUTHENTICATION);
        Assert.notNull(token, "token为空");
        String userId = JwtUtils.getUserId(token);
        Assert.notNull(userId, "鉴权异常");
        // 判断登录是否过期
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        Assert.notNull(redisService, "获取bean失败");
        if (Boolean.FALSE.equals(redisService.hasKey(getTokenKey(token)))) {
            throw new ServiceException("登录状态已过期");
        }
        AccountService accountService = SpringUtils.getBean(AccountService.class);
        Assert.notNull(accountService, "获取bean失败");
        Account account = accountService.getById(userId);
        Assert.notNull(account, "账户不存在");
        // 判断账号状态
        if (account.getState() == Constants.DISABLE || account.getDeleted() == Constants.DISABLE) {
            throw new ServiceException("账号已被封禁/删除");
        }
        // 登录过期时间还剩50分钟,自动刷新时间
        refreshTokenTime(token);
        SecurityContextUtil.setLoginSubject(JwtUtils.getLoginUser(token));
        log.info("鉴权成功放行接口,url: {}", request.getRequestURI());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("清楚登录用户数据");
        SecurityContextUtil.clearLoginSubject();
    }

    /**
     * 获取缓存key
     */
    private String getTokenKey(String token) {
        return Constants.LOGIN_TOKEN_KEY + token;
    }

    private void refreshTokenTime(String token) {
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        long expireTime = redisService.getExpire(getTokenKey(token));
        if (expireTime <= MILLIS_MINUTE_TEN) {
            // 刷新key缓存过期时间
            LoginSubject loginUserDTO = redisService.getCacheObject(getTokenKey(token));
            redisService.setCacheObject(Constants.LOGIN_TOKEN_KEY + token, loginUserDTO, Constants.OUT_TIME, TimeUnit.SECONDS);
            log.info("token过期时间刷新成功");
        }
    }
}
